#include "render_device.h"
#include "material.h"
#include "entity.h"
#include "mesh.h"
#include "scene.h"
#include "camera.h"

c_mesh_entity::c_mesh_entity(c_scene *scene_mgr, const std::string& name, mesh_ptr mesh_ptr)
    : c_scene_node(scene_mgr, name)
    , m_mesh_ptr(mesh_ptr)
{
    
}

c_mesh_entity::~c_mesh_entity()
{
}

bool c_mesh_entity::update(c_scene *scene, const float dt)
{
    return super::update(scene, dt);
}

void c_mesh_entity::pre_render(c_scene *scene_mgr)
{
    
}

void c_mesh_entity::render(c_scene *scene_mgr)
{
    d3d9_vb_ptr vb = m_mesh_ptr->get_d3d9_vb_ptr(); 
    d3d9_ib_ptr ib = m_mesh_ptr->get_d3d9_ib_ptr(); 
    unsigned long size_of_vertex = m_mesh_ptr->get_d3dx_mesh_ptr()->GetNumBytesPerVertex(); 
    
    d3d9_device_ptr& d3d9_device = c_render_device9::get_singleton().get_d3d_device_ptr();

    //V(d3d9_device->SetVertexDeclaration());
    //V(d3d9_device->SetStreamSource(0, vb, 0, size_of_vertex)); 
    //V(d3d9_device->SetIndices(ib));

    // for each sub-mesh:
    c_d3dx_mesh::submesh_iterator it;
    unsigned int submesh_idx = 0;
    for (it = m_mesh_ptr->submesh_begin(); it != m_mesh_ptr->submesh_end(); ++it, ++submesh_idx)
    {
        unsigned int num_passes = (*it)->get_material()->begin_effect();

        upload_gpu_parameters((*it)->get_material());
        
        for (unsigned int i = 0; i < num_passes; ++i)
        {
            (*it)->get_material()->begin_pass(i);
            
            (*it)->get_material()->get_d3dx_effect_ptr()->CommitChanges(); 

/*            render_op op;
            (*it)->get_render_op(op);
            
            V(d3d9_device->DrawIndexedPrimitive(
                op.type, 
                op.base_vertex_index, 
                op.min_index, 
                op.num_vertices, 
                op.start_index, 
                op.num_primitives));   */ 
            
            get_mesh()->get_d3dx_mesh_ptr()->DrawSubset(submesh_idx);
            
            (*it)->get_material()->end_pass();
        }
        
        (*it)->get_material()->end_effect(); 
    }
}

void c_mesh_entity::post_render(c_scene *scene_mgr)
{
    
}

void c_mesh_entity::upload_gpu_parameters(material_ptr& material)
{
    /** upload default parameters
        world-view-projection matrix
        world inverse transpose matrix
    */
    camera_ptr cam = m_scene_mgr->get_singleton().get_active_camera();
    c_matrix4x4 mat_world = calc_world_transform();
    c_matrix4x4 mat_view = cam->get_view(); 
    c_matrix4x4 mat_proj = cam->get_proj(); 
    
    material->set_world_view_proj(mat_world, mat_view, mat_proj);
    material->set_camera_params(cam);
    
    upload_gpu_parameters_impl(material); 
}

void c_mesh_entity::upload_gpu_parameters_impl(material_ptr& material)
{
    
}

mesh_entity_ptr make_entity(const std::string& name, const mesh_ptr& mesh)
{
    mesh_entity_ptr ent(new c_mesh_entity(c_scene::get_singleton_ptr(), name, mesh));
    return ent; 
}

mesh_entity_ptr make_simple_cube(const material_ptr& material)
{
	mesh_ptr cube_mesh_ptr = create_mesh_from_x_file("./data/models/simple_cube/cube.x");
	mesh_entity_ptr cube_ent(new c_mesh_entity(c_scene::get_singleton_ptr(), std::string("cube"), cube_mesh_ptr)); 
    
    c_d3dx_mesh::submesh_iterator it;
    for (it = cube_mesh_ptr->submesh_begin(); it != cube_mesh_ptr->submesh_end(); ++it)
    {
        (*it)->set_material(material);
    }
    
	return cube_ent; 
}